package main

import (
	"fmt"
	"os"
	"syscall"
	"os/exec"
	"io"
	"bufio"
	"strings"
	flag "github.com/spf13/pflag"
)

type selpgArgs struct {
    	startPage int
    	endPage   int
    	inputFile string
    	pageLen   int
    	pageType  bool
    	printDest string
}

var sa = new(selpgArgs)

func init() {
	flag.IntVarP(&sa.startPage, "s", "s", -1, "start page")
	flag.IntVarP(&sa.endPage, "e", "e", -1, "end page")
	flag.IntVarP(&sa.pageLen, "l", "l", 72, "lines per page")
	flag.BoolVarP(&sa.pageType, "f", "f", false, "form-feed-delimited")
	flag.StringVarP(&sa.printDest, "d", "d", "", "print destination")
	flag.Usage = usage
}

func processArgs() {
    	if flag.NFlag() < 1 {
    		/* handle mandatory args first */
	} else if sa.startPage < 1 {
		fmt.Fprintf(os.Stderr, "invalid start page %v\n", sa.startPage)
    	} else if sa.endPage < 1 || sa.endPage < sa.startPage {
		fmt.Fprintf(os.Stderr, "invalid end page %v\n", sa.endPage)
    	} else if !sa.pageType && sa.pageLen < 1 {
		fmt.Fprintf(os.Stderr, "invalid page length %v\n", sa.pageLen)
	} else {
		if flag.NArg() > 0 {
	    		sa.inputFile = flag.Arg(0)
	    		if syscall.Access(sa.inputFile, syscall.O_RDONLY) != nil {
				fmt.Fprintf(os.Stderr, "input file \"%s\" does not exist or cannot be read\n", sa.inputFile)				
	    		} else { return }
		} else { return }
    	}
    	flag.Usage()
    	os.Exit(1)
}

func processInput() {
    	fin := os.Stdin
    	fout := os.Stdout
    	lineCount := 0
    	pageCount := 1
    	var inpipe io.WriteCloser
    	var err error

    	if sa.inputFile != "" {
		fin, err = os.Open(sa.inputFile)
    	}

    	if sa.printDest != "" {
		cmd := exec.Command("lp", "-d", sa.printDest)
		inpipe, err = cmd.StdinPipe()
		if err != nil {
	    		fmt.Fprintf(os.Stderr, "could not open pipe to \"%s\"\n", sa.printDest)				
	    		flag.Usage()
	    		os.Exit(1)
		}
		cmd.Stdout = fout
		cmd.Start()
    	}

    	if sa.pageType {
		reader := bufio.NewReader(fin)
    		for {
	    		page, rerr := reader.ReadString('\f')
	    		if pageCount >= sa.startPage {
	        		page = strings.Replace(page, "\f", "", -1)
	        		if sa.printDest != "" {
		    			fmt.Fprintf(inpipe, "%s", page)
	        		} else {
		    			fmt.Fprintf(fout, "%s", page)
	        		}
	    		}
	    		pageCount++
	    		if rerr == io.EOF || pageCount > sa.endPage {
	        		break
	    		}
		}
    	} else {
		line := bufio.NewScanner(fin)
		for line.Scan() {
	    		if pageCount >= sa.startPage {
				if sa.printDest != "" {
		    			fmt.Fprintf(inpipe, "%s\n", line.Text())
				} else {
		    			fmt.Fprintf(fout, "%s\n", line.Text())
				}
	    		}
	    		lineCount++
	   		if lineCount == sa.pageLen {
				lineCount = 0
				pageCount++
				if pageCount > sa.endPage {
		    			break
				}
	    		}
      		}
    	}


    	if pageCount < sa.startPage {
		fmt.Fprintf(os.Stderr, "start_page (%d) larger than total pages (%d), no output written\n", sa.startPage, pageCount)
    	} else if pageCount < sa.endPage {
		fmt.Fprintf(os.Stderr, "end_page (%d) larger than total pages (%d), less output than expected\n", sa.endPage, pageCount)
    	} else {
		fin.Close()
		if sa.printDest != "" {
	    		inpipe.Close()
		}
		fmt.Fprintf(os.Stderr, "done\n");
    	}
}

func usage() {
    	fmt.Fprintf(os.Stderr, `Usage: ./selpg [-s start_page] [-e end_page] [ -f | -l lines_per_page ] [ -d dest ] [ in_filename ] Options:`)
    	flag.PrintDefaults()
}

func main() {
    	flag.Parse()
    	processArgs()
    	processInput()
}

